<!DOCTYPE html>
<!--
Copyright 2011 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Author: Eric Bidelman (ericbidelman@chromium.org)
-->
<html>
<head>
  <title>Introduction to WebGL</title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="chrome=1">
  <script src='js/config.js'></script>
  <script src='js/slides.js'></script>

  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:regular,semibold,italic,italicsemibold|Droid+Sans+Mono&v2">
</head>
<body style="display: none">

<section class='slides layout-regular'>

  <article class="title-slide">
    <div>
      <div data-config-logo></div>
      <p data-config-name></p>
      <p style="color:#545454;">Enter the Third Dimension:</p>
      <h1>Introduction to WebGL</h1>
      <p class="info">
        <time datetime="YYYY-MM-DD" data-config-date>MM DD, 2011</time> &nbsp; - &nbsp; <span data-config-location></span>
      </p>
      <img data-config-map class="rounded" title="Of course we're using Geolocation!" alt="Of course we're using Geolocation!">
    </div>
  </article>
  
  <article id="who">
    <h3>Who?</h3>
    <p>
      <img class="avatar rounded" data-config-pic>
    </p>
    <p>
      <a rel="author" data-config-gplus target="_blank">
        <img src="http://www.google.com/images/icons/ui/gprofile_button-44.png" width="44" height="44">
      </a> +<a rel="author" data-config-gplus target="_blank"><span data-config-name></span></a>
    </p>
    <br>
    <p>
      <a rel="author" data-config-twitter target="_blank" style="margin-left:-8px;">
        <img src="images/twitter_newbird_blue.png" width="58" height="58">
      </a> @ <a rel="author" data-config-twitter target="_blank" style="margin-left:-8px;"><span data-config-twitter></span></a>
    </p>
    <p>
      <a rel="author" data-config-blog target="_blank"><span data-config-blog></span></a>
    </p>
  </article>

  <article>
    <h3>Today's agenda</h3>
    <ul>
      <li>An overview of WebGL</li>
      <li>Small Three.js tutorial</li>
    </ul>
  </article>

  <article class="title">
    <h2>WebGL</h2>
  </article>

  <article>
    <h3>What is WebGL?</h3>
    <ul>
      <li>A Khronos group standard</li>
      <li>JavaScript meets the GPU</li>
      <li>Fast programmable drawing</li>
      <li>OpenGL ES 2.0 for HTML5 Canvas</li>
    </ul>
  </article>

  <article>
    <h3>What can you do with it?</h3>
    <ul><li>Shader-based drawing</li></ul>
    <br><br>
    <h3>What?</h3>
    <ul>
      <li>2D graphics</li>
      <li>3D graphics</li>
    </ul>
    <br>
    <p>But in a very <i>generic</i> fashion.</p>
  </article>

  <article>
    <h3>Basic idea</h3>
    <p>Send a list of points to WebGL</p>
    <p>Vertex shader places them on the screen</p>
    <p>Fragment shader colors the pixels using vertex shader output</p>
  </article>

  <article>
    <h3>And...?</h3>
    <p>That's pretty much it.</p>
    <p>WebGL is just a fast way to put lots of triangles, lines and points on the screen.</p>
    <p>How to transform the vertices and shade the pixels is up to the programmer.</p>
  </article>

  <article class="nobackground">
    <h3>You can either do this, or...</h3>
    <iframe src="three/three_3.html"></iframe>
  </article>

  <article class="nobackground">
    <h3>...this.</h3>
    <iframe src="three/three_6.html"></iframe>
  </article>

  <article>
    <h3>What browsers support it?</h3>
      <p>Chrome</p>
      <p>Firefox</p>
      <p>Safari (switch to Developer mode, enable WebGL from menu)</p>
      <p>Opera 12+</p>
      <p>Chrome Frame</p>
      <p>IEWebGL, cWebGL</p>
  </article>

  <article>
    <h3>What can you use it for?</h3>
      <p>Games</p>
      <p>Image filters</p>
      <p>Data visualization</p>
      <p>3D applications</p>
      <p>And other things that need fast programmable drawing</p>
  </article>

  <article>
    <h3>Let's talk about libraries</h3>
    <p>Get things done faster</p>
    <p>Write less boilerplate</p>
    <p>Let's talk about three.js</p>
  </article>


  <article class="title">
    <h2>Three.js?</h2>
  </article>

  <article>
    <h3>Three.js is a JS 3D Engine</h3>
    <ul>
      <li><a href="https://github.com/mrdoob/three.js/">github.com/mrdoob/three.js</a></li>
      <li>Lightweight</li>
      <li>Easy to use</li>
      <li>Batteries included</li>
      <li>WebGL renderer (our focus today)</li>
      <li>Also has Canvas and SVG renderers</li>
    </ul>
  </article>

  <article class="title">
    <h2>Basic setup</h2>
  </article>

  <article>
    <h3>Renderer</h3>
    <p>Create a WebGLRenderer</p>
    <pre>
      var renderer = new THREE.WebGLRenderer({antialias: true});
      renderer.setSize(document.body.clientWidth, 
                       document.body.clientHeight);</pre>
    <p>Plug it in</p>
    <pre>
      document.body.appendChild(renderer.domElement);</pre>
    <p>And make it pretty</p>
    <pre>
      renderer.setClearColorHex(0xEEEEEE, 1.0);
      renderer.clear();</pre>
  </article>

  <article class="nobackground">
    <h3>Wow!</h3>
    <iframe src="three/three_1.html"></iframe>
  </article>

  <article>
    <h3>No, wait, it gets better!</h3>
    <div>
    <p>Create a Camera</p>
    <pre>
// new THREE.PerspectiveCamera(FOV, viewAspectRatio, zNear, zFar);
var camera = new THREE.PerspectiveCamera(45, width/height, 1, 10000);
camera.position.z = 300;</pre>
    <p>Make a Scene with a Cube</p>
    <pre>
var scene = new THREE.Scene();
var cube = new THREE.Mesh(new THREE.CubeGeometry(50,50,50),
               new THREE.MeshBasicMaterial({color: 0x000000}));
scene.add(cube);</pre>
    <p>And render the Scene from the Camera</p>
    <pre>
      renderer.render(scene, camera);</pre>
    </div>
  </article>

  <article class="nobackground">
    <h3>At least it isn't empty.</h3>
    <iframe src="three/three_2.html"></iframe>
  </article>

  <article class="title">
    <h2>Animation</h2>
  </article>

  <article>
    <h3>Can we make it move?</h3>
    <p>Yes we can!</p>
    <p class="small">(But it's a manual process.)</p>
    <pre>
      function animate(t) {
        // spin the camera in a circle
        camera.position.x = Math.sin(t/1000)*300;
        camera.position.y = 150;
        camera.position.z = Math.cos(t/1000)*300;
        // you need to update lookAt on every frame
        camera.lookAt(scene.position);
        // renderer automatically clears unless autoClear = false
        renderer.render(scene, camera);
        window.requestAnimationFrame(animate, renderer.domElement);
      };
      animate(new Date().getTime());
    </pre>
  </article>

  <article class="nobackground">
    <h3>Isn't it nice?</h3>
    <iframe src="three/three_3.html"></iframe>
  </article>

  <article class="title">
    <h2>Lighting</h2>
  </article>

  <article>
    <h3>Lights</h3>
    <p>Without light, we live in the dark.</p>
    <p>Let's create a Light</p>
    <pre>
      var light = new THREE.SpotLight();
      light.position.set( 170, 330, -160 );
      scene.add(light);</pre>
    <p>And a lit cube</p>
    <pre>
      var litCube = new THREE.Mesh(
        new THREE.CubeGeometry(50, 50, 50),
        new THREE.MeshLambertMaterial({color: 0xFFFFFF}));
      litCube.position.y = 50;
      scene.add(litCube);</pre>
  </article>

  <article class="nobackground">
    <h3>There we go!</h3>
    <iframe src="three/three_4.html"></iframe>
  </article>

  <article>
    <h3>How about shadows?</h3>
    <p>Three.js has shadow maps.</p>
    <p>You need to enable them per-light and per-object.</p>
    <p>The shadows only work on SpotLights.</p>
    <pre>
      // enable shadows on the renderer
      renderer.shadowMapEnabled = true;

      // enable shadows for a light
      light.castShadow = true;

      // enable shadows for an object
      litCube.castShadow = true;
      litCube.receiveShadow = true;</pre>
  </article>

  <article>
    <h3>Added interest</h3>
    <p>Let's add a ground plane</p>
    <pre>
  var planeGeo = new THREE.PlaneGeometry(400, 200, 10, 10);
  var planeMat = new THREE.MeshLambertMaterial({color: 0xFFFFFF});
  var plane = new THREE.Mesh(planeGeo, planeMat);
  plane.rotation.x = -Math.PI/2;
  plane.position.y = -25;
  plane.receiveShadow = true;
  scene.add(plane);</pre>
    <p>And make the cube spin</p>
    <pre>
  litCube.position.x = Math.cos(t/600)*85;
  litCube.position.y = 60-Math.sin(t/900)*25;
  litCube.position.z = Math.sin(t/600)*85;
  litCube.rotation.x = t/500;
  litCube.rotation.y = t/800;</pre>
  </article>

  <article class="nobackground">
    <h3>Plus a couple of tweaks</h3>
    <iframe src="three/three_5.html"></iframe>
  </article>

  <article>
    <h3>Lighting recap</h3>
    <p>Set mesh material to <code>new THREE.MeshLambertMaterial</code> or <code>new THREE.MeshPhongMaterial</code></p>
    <p>Create a light <code>new THREE.SpotLight(color)</code></p>
    <p>Add the light to the scene <code>scene.add(light)</code></p>
    <p>Turn on shadows if you need them</p>
    <pre>
      scene.shadowMapEnabled = true;
      light.castShadow = true;
      object.castShadow = true;
      object.receiveShadow = true;</pre>
  </article>

  <article class="title">
    <h2>Useful uses</h2>
  </article>

  <article>
    <h3>Bar graph</h3>
    <pre>
      var grid = /* 2D Array */
      var barGraph = new THREE.Object3D();
      scene.add(barGraph);

      var max = /* Grid max value */
      var mat = new THREE.MeshLambertMaterial({color: 0xFFAA55});
      for (var j=0; j&lt;grid.length; j++) {
        for (var i=0; i&lt;grid[j].length; i++) {
          var barHeight = grid[j][i]/max * 80;
          var geo = new THREE.CubeGeometry(8, barHeight, 8);
          var mesh = new THREE.Mesh(geo, mat);
          mesh.position.x = (i-grid[j].length/2) * 16;
          mesh.position.y = barHeight/2;
          mesh.position.z = -(j-grid.length/2) * 16;
          mesh.castShadow = mesh.receiveShadow = true;
          barGraph.add(mesh);
        }
      }</pre>
  </article>
  
  <article class="nobackground">
    <h3>Bar graph in action</h3>
    <iframe src="three/three_7.html"></iframe>
  </article>

  <article class="nobackground">
    <h3>Scatter plot</h3>
    <pre>
var scatterPlot = new THREE.Object3D();
var mat = new THREE.ParticleBasicMaterial(
  {vertexColors: true, size: 1.5});

var pointCount = 10000;
var pointGeo = new THREE.Geometry();
for (var i=0; i&lt;pointCount; i++) {
  var x = Math.random() * 100 - 50;
  var y = x*0.8+Math.random() * 20 - 10;
  var z = x*0.7+Math.random() * 30 - 15;
  pointGeo.vertices.push(new THREE.Vertex(new THREE.Vector3(x,y,z)));
  pointGeo.colors.push(new THREE.Color().setHSV(
    (x+50)/100, (z+50)/100, (y+50)/100));
}
var points = new THREE.ParticleSystem(pointGeo, mat);
scatterPlot.add(points);
scene.fog = new THREE.FogExp2(0xFFFFFF, 0.0035);</pre>
  </article>

  <article class="nobackground">
    <h3>Scatter plot in action</h3>
    <iframe src="three/three_8.html"></iframe>
  </article>

  <article class="title">
    <h2>GUI controls</h2>
  </article>

  <article>
    <h3>GUI controls</h3>
    <p>To me, GUI controls have been a pain.</p>
    <p>Write your own widgets, write your own onchange handlers, write your own interval change polling logic, style it all, hope it scales, gaaaaaah!</p>
    <p>Let's just use DAT.GUI instead.</p>
    <pre>
      var gui = new DAT.GUI();
      gui.add(cube.scale, 'x').min(0.1).max(10).step(0.1);
      gui.add(cube.scale, 'y', 0.1, 10, 0.1);
      gui.add(cube.scale, 'z', 0.1, 10, 0.1);</pre>
    <p>Done!</p>
    <p><a href="http://code.google.com/p/dat-gui">code.google.com/p/dat-gui</a></p>
  </article>

  <article class="nobackground">
    <h3>Scalable cube</h3>
    <iframe src="three/three_11.html"></iframe>
  </article>

  <article>
    <h3>But but, where is the <i>pain</i>?</h3>
    <p>Well, we could do a proxy to control only the currently selected object.</p>
    <pre>
var controller = new THREE.Object3D();
var gui = new DAT.GUI({width: 160});

controller.setCurrent = function(current) {
  this.current = current;
  this.x.setValue(current.position.x);
  this.y.setValue(current.position.y);
  this.z.setValue(current.position.z);
};
      
controller.x = gui.add(controller.position, 'x').onChange(function(v){
  controller.current.position.x = v;
});
// etc.</pre>
  </article>

  <article>
    <h3>How to select?</h3>
    <p>Project a ray into the scene and find intersecting objects.</p>
    <pre>
var projector = new THREE.Projector();
window.addEventListener('mousedown', function (ev){
  if (ev.target == renderer.domElement) {
    var x = ev.clientX;
    var y = ev.clientY;
    var v = new THREE.Vector3((x/width)*2-1, -(y/height)*2+1, 0.5);
    projector.unprojectVector(v, camera);
    var ray = new THREE.Ray(camera.position, 
                            v.subSelf(camera.position).normalize());
    var intersects = ray.intersectObjects(controller.objects);
    if (intersects.length > 0) {
      controller.setCurrent(intersects[0].object);
    }
  }
}, false);</pre>
  </article>

  <article class="nobackground">
    <h3>Great cubistan</h3>
    <iframe src="three/three_12.html"></iframe>
  </article>

  <article class="title">
    <h2>Loading models</h2>
  </article>

  <article>
    <h3>COLLADA loader</h3>
    <pre>
new THREE.ColladaLoader().load('models/monster.dae', 
function(collada) {
  var model = collada.scene;
  model.scale.set(0.1, 0.1, 0.1);
  model.rotation.x = -Math.PI/2;
  model.updateMatrix();
  scene.add(model);
});</pre>
    <p>Doesn't look too complicated.</p>
    <p>Copy-pasted from examples/webgl_collada.html</p>
    <p>Along with the code to make it animate.</p>
  </article>

  <article class="nobackground">
    <h3>Monster</h3>
    <iframe src="three/three_13.html"></iframe>
  </article>

  <article class="title">
    <h2>Conclusion</h2>
  </article>

  <article>
    <h3>Three.js</h3>
    <p><a href="https://github.com/mrdoob/three.js/">github.com/mrdoob/three.js</a></p>
    <p>JavaScript library for 3D graphics</p>
    <p>Easy to use</p>
    <p>Efficient</p>
    <p>Nice feature set</p>
    <p>DAT.GUI for simple GUIs <a href="http://code.google.com/p/dat-gui">code.google.com/p/dat-gui</a></p>
  </article>
  
  <article class="title-slide">
    <div>
      <div data-config-logo></div>
      <p style="color:#545454;">Thanks!</p>
      <h3 class="questions">Questions?</h3>
      <p style="margin-top:0px;">
        <a rel="author" data-config-gplus target="_blank" style="margin-right:10px;text-decoration:none;">+<span data-config-name></span></a>
        <a rel="author" data-config-twitter target="_blank" style="text-decoration:none;">@<span data-config-twitter></span></a>
      </p>
      <p class="info" style="margin-top:5em;font-size:100%;line-height:1.25em;">
        Slides: <a data-config-slides target="_blank"><span data-config-slides></span></a><br>
        <!--Feedback: <a data-config-feedback target="_blank"><span data-config-feedback></span></a><br>-->
      </p>
    </div>
  </article>

</section>

<!--
<script> 
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXXX-X']);
  _gaq.push(['_trackPageview']);
 
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
</script> 
<!--[if IE]>
<script src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script>CFInstall.check({mode: 'overlay'});</script>
<![endif]-->

</body>
</html>
